package Parse;

import java_cup.runtime.*;

%%

%class Lexer
%unicode
%public
%cup
%type java_cup.runtime.Symbol
%char
%{
int CommentCount;
StringBuffer string = new StringBuffer();

private void newline() {
  errorMsg.newline(yychar);
}

private void err(int pos, String s) {
  errorMsg.error(pos,s);
}

private void err(String s) {
  err(yychar,s);
}

private java_cup.runtime.Symbol tok(int kind, Object value) {
    return new java_cup.runtime.Symbol(kind, yychar, yychar+yylength(), value);
}

private java_cup.runtime.Symbol tok(int kind) {
    return new java_cup.runtime.Symbol(kind, yychar, yychar+yylength(), null);
}

private ErrorMsg.ErrorMsg errorMsg;

public Lexer(java.io.InputStream s, ErrorMsg.ErrorMsg e) {
  this(s);
  errorMsg=e;
}

%}

LineTerminator = \r|\n|\r\n
InputCharacter = [^\r\n]
WhiteSpace = {LineTerminator} | [ \t\f]


Identifier = [a-zA-Z][a-zA-Z0-9_]*
IntegerLiteral = [0-9]+

%state STRING
%state COMMENT

%%
/* keywords */
<YYINITIAL> "array" { return tok(sym.ARRAY); }
<YYINITIAL> "break" { return tok(sym.BREAK); }
<YYINITIAL> "do" { return tok(sym.DO); }
<YYINITIAL> "else" { return tok(sym.ELSE); }
<YYINITIAL> "end" { return tok(sym.END); }
<YYINITIAL> "for" { return tok(sym.FOR); }
<YYINITIAL> "function" { return tok(sym.FUNCTION); }
<YYINITIAL> "if" { return tok(sym.IF); }
<YYINITIAL> "in" { return tok(sym.IN); }
<YYINITIAL> "let" { return tok(sym.LET); }
<YYINITIAL> "nil" { return tok(sym.NIL); }
<YYINITIAL> "of" { return tok(sym.OF); }
<YYINITIAL> "then" { return tok(sym.THEN); }
<YYINITIAL> "to" { return tok(sym.TO); }
<YYINITIAL> "type" { return tok(sym.TYPE); }
<YYINITIAL> "var" { return tok(sym.VAR); }
<YYINITIAL> "while" { return tok(sym.WHILE); }

<YYINITIAL> {
	/* identifiers */
	{Identifier} { return tok(sym.ID, yytext()); }

	/* literals */
	{IntegerLiteral} { return tok(sym.INT, Integer.parseInt(yytext())); }

	\" { string.setLength(0); yybegin(STRING); }
	"/*"	{	CommentCount = 0; yybegin(COMMENT); }

	/* operators */
	"=" { return tok(sym.EQ); }
	"+" { return tok(sym.PLUS); }
	"-" { return tok(sym.MINUS); }
	"*" { return tok(sym.STAR); }
	"/" { return tok(sym.SLASH); }
	"," { return tok(sym.COMMA); }
	":" { return tok(sym.COLON); }
	";" { return tok(sym.SEMICOLON); }
	"(" { return tok(sym.LPAREN); }
	")" { return tok(sym.RPAREN); }
	"[" { return tok(sym.LBRAC); }
	"]" { return tok(sym.RBRAC); }
	"{" { return tok(sym.LBRACE); }
	"}" { return tok(sym.RBRACE); }
	"." { return tok(sym.DOT); }
	"<>" { return tok(sym.NEQ); }
	"<" { return tok(sym.LT); }
	"<=" { return tok(sym.LTEQ); }
	">" { return tok(sym.GT); }
	">=" { return tok(sym.GTEQ); }
	"&" { return tok(sym.AND); }
	"|" { return tok(sym.OR); }
	":=" { return tok(sym.COLONEQ); }

	/* whitespace */
	{LineTerminator} { newline(); }
	[ \t\f] { }
	
	[^] { err("Unexpected character."); }
}
<STRING> {
	\" { yybegin(YYINITIAL); return tok(sym.STRING, string.toString()); }
	[^\"\\\r\n]+ { string.append( yytext() ); }
	\\t { string.append('\t'); }
	\\n { string.append('\n'); }
	\\[0-9]{3} {  String t = yytext(); 
		int a = Integer.parseInt(t.substring(t.length()-3, 4));
		
		if (a < 0 | a > 255) err("Unknown escape sequence.");
			else string.append((char)a);
	}
	\\\"  { string.append('\"');}
	\\\\ { string.append('\\'); }
	\\{WhiteSpace}+\\ {
		String t = yytext();
		int i;
		
		for (i = 0; i < t.length() - 1; i++)
		if (t.charAt(i) == '\n' || t.charAt(i) == '\r' && t.charAt(i + 1) != '\n')
			newline();
	}
	\\[^] { err("Unknown escape sequence."); }
	{LineTerminator} { err("Unclosed string."); newline(); }
	<<EOF>> { err("Unclosed string."); return tok(sym.EOF);}
	[^] { err("Unexpected character."); }
}
<COMMENT> {
	"/*"	{CommentCount++;}
	"*/"	{if (CommentCount == 0) yybegin(YYINITIAL); else CommentCount--;}
	. {}
	{LineTerminator} { newline(); }
	<<EOF>> { err("Unclosed Comment."); return tok(sym.EOF);}
}

/* error fallback */
/*.|\n { throw new Error("Illegal character <"+yytext()+">"); }*/
